/******************************************************************************
 * Copyright (c) 2006, 2010 VMware Inc.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * and Apache License v2.0 which accompanies this distribution. 
 * The Eclipse Public License is available at 
 * http://www.eclipse.org/legal/epl-v10.html and the Apache License v2.0
 * is available at http://www.opensource.org/licenses/apache2.0.php.
 * You may elect to redistribute this code under either of these licenses. 
 *
 * Contributors:
 *   VMware Inc.
 *****************************************************************************/

package org.eclipse.gemini.blueprint.test.internal.support;

import junit.framework.Protectable;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestResult;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.gemini.blueprint.test.internal.OsgiJUnitTest;
import org.eclipse.gemini.blueprint.test.internal.TestRunnerService;
import org.eclipse.gemini.blueprint.test.internal.holder.HolderLoader;
import org.eclipse.gemini.blueprint.test.internal.holder.OsgiTestInfoHolder;
import org.eclipse.gemini.blueprint.test.internal.util.TestUtils;

/**
 * OSGi service for executing JUnit tests.
 *
 * @author Costin Leau
 */
public class OsgiJUnitService implements TestRunnerService {

    private static final Log log = LogFactory.getLog(OsgiJUnitService.class);


    public void runTest(OsgiJUnitTest test) {
        try {
            executeTest(test);
        } catch (Exception ex) {
            if (ex instanceof RuntimeException) {
                throw (RuntimeException) ex;
            }
            throw new RuntimeException("cannot execute test:" + ex, ex);
        }
    }

    /**
     * Execute the JUnit test and publish results to the outside-OSGi world.
     *
     * @param test
     * @throws Exception
     */
    protected void executeTest(OsgiJUnitTest test) throws Exception {
        // create holder
        // since we're inside OSGi, we have to use the special loading procedure
        OsgiTestInfoHolder holder = HolderLoader.INSTANCE.getHolder();

        // read the test to be executed
        String testName = holder.getTestMethodName();
        if (log.isDebugEnabled()) {
            log.debug("Reading test [" + testName + "] for execution inside OSGi");
        }
        // execute the test
        TestResult result = runTest(test, testName);

        if (log.isDebugEnabled()) {
            log.debug("Sending test results from OSGi");
        }
        // write result back to the outside world
        TestUtils.unpackProblems(result, holder);
    }

    /**
     * Run fixture setup, test from the given test case and fixture teardown.
     *
     * @param osgiTestExtensions
     * @param testName
     */
    protected TestResult runTest(final OsgiJUnitTest osgiTestExtensions, String testName) {
        if (log.isDebugEnabled()) {
            log.debug("Running test [" + testName + "] on testCase " + osgiTestExtensions);
        }

        final TestResult result = new TestResult();
        TestCase rawTest = osgiTestExtensions.getTestCase();
        rawTest.setName(testName);

        try {
            osgiTestExtensions.osgiSetUp();

            try {
                // use TestResult method to bypass the setUp/tearDown methods
                result.runProtected(rawTest, new Protectable() {

                    public void protect() throws Throwable {
                        osgiTestExtensions.osgiRunTest();
                    }

                });
            } finally {
                osgiTestExtensions.osgiTearDown();
            }
        }
        // exceptions thrown by osgiSetUp/osgiTearDown
        catch (Exception ex) {
            log.error("test exception threw exception ", ex);
            result.addError(rawTest, ex);
        }
        return result;
    }
}